1 module repl; 2 3 import std.algorithm; 4 import pegged.grammar; 5 import sumtype; 6 import std.format; 7 import std.conv; 8 import std.traits; 9 import std.meta; 10 import std.typecons; 11 12 mixin(grammar(` 13 ToyLanguage: 14 Statements < ((AssignmentExpression / Expression) (EndOfLine?))+ eoi 15 16 AssignmentExpression < Expression "=" (Value / Expression) 17 18 Expression < (Invoke / Identifier / IncompleteIdentifier) ("." (Invoke / Identifier / IncompleteIdentifier))* 19 20 Invoke <- Identifier ("()") 21 22 Whitespace <- :(' ' / '\t')+ 23 24 EndOfLine <: ('\r' '\n') / '\n' 25 26 Spacing <- :(Whitespace)* 27 28 Identifier <~ '_'? [A-Za-z] [0-9A-Z_a-z-]* 29 30 IncompleteIdentifier < eps 31 32 Integer <~ '-'? ([1-9] [0-9]*) / ('0' [Xx] [0-9A-Fa-f]+) / ('0' [0-7]*) 33 34 Float <~ '-'? ((([0-9]+ '.' [0-9]*) / ([0-9]* '.' [0-9]+))(('E' / 'e') ('+' / '-')? [0-9]+)?) / ([0-9]+ ('E' / 'e') ('+' / '-')? [0-9]+) 35 36 String <~ "\"" (!("\"" / EndOfLine) .)* "\"" 37 38 Value <- String / Float / Integer 39 `)); 40 41 auto parseToyLanguage(string code) @trusted { 42 return ToyLanguage(code); 43 } 44 45 @safe unittest { 46 assert(parseToyLanguage("a").successful); 47 assert(parseToyLanguage("a = 4").successful); 48 assert(parseToyLanguage(`a = "text"`).successful); 49 assert(parseToyLanguage("a = FloatWidget()").successful); 50 assert(parseToyLanguage("a.max = 45.3").successful); 51 assert(parseToyLanguage("a.display()").successful); 52 assert(parseToyLanguage(`a = FloatWidget() 53 a.max = 45.3 54 a.display()`).successful); 55 } 56 57 version (unittest) { 58 struct Foo { 59 int a; 60 void doStuff() @safe {a = 5; } 61 void set(int b) @safe { a = b; } 62 } 63 struct Inner { 64 int b = 1; 65 void incr() @safe { b += 1; } 66 } 67 struct Bar { 68 Inner inner; 69 } 70 } 71 72 @safe unittest { 73 Repl!(Foo) repl; 74 75 repl.run("a"); 76 repl.run("a = Foo()"); 77 assert(repl.run("a.a").value == repl.Value(0)); 78 repl.run("a.doStuff()"); 79 assert(repl.run("a.a").value == repl.Value(5)); 80 } 81 82 @safe unittest { 83 Repl!(Bar) repl; 84 85 repl.run(`a = Bar()`); 86 assert(repl.run(`a.inner.b`).value == repl.Value(1)); 87 repl.run(`a.inner.incr()`); 88 assert(repl.run(`a.inner.b`).value == repl.Value(2)); 89 assert(repl.run(`a.inner.b = 4`).value == repl.Value(4)); 90 repl.run(`a.inner.incr()`); 91 assert(repl.run(`a.inner.b`).value == repl.Value(5)); 92 } 93 94 @safe unittest { 95 Repl!(Foo) repl; 96 97 repl.addGlobal("foo", Foo(4)); 98 assert(repl.run("foo.a").value == repl.Value(4)); 99 } 100 101 @safe unittest { 102 Repl!(Foo) repl; 103 104 repl.run("a = Foo()"); 105 assert(repl.run("a.a = 43.3").error == true); 106 assert(repl.run("a.a = 43.3").message == "Error: cannot assign type double to type int in 'a.a = 43.3'"); 107 assert(repl.run("a.noexist").error == true); 108 assert(repl.run("a.noexist").message == "Error: property 'noexist' is not available on type Foo in 'noexist'"); 109 assert(repl.run("a.noexist()").error == true); 110 assert(repl.run("a.noexist()").message == "Error: method 'noexist' is not available on type Foo in 'noexist()'"); 111 assert(repl.run("Nope()").error == true); 112 assert(repl.run("Nope()").message == "Error: type Nope not found in 'Nope()'"); 113 assert(repl.run("a.a.impossible").error == true); 114 assert(repl.run("a.a.impossible").message == "Error: cannot access a property on a int in 'impossible'"); 115 assert(repl.run("a.a.impossible()").error == true); 116 assert(repl.run("a.a.impossible()").message == "Error: cannot call a method on a int in 'impossible()'"); 117 } 118 119 @safe unittest { 120 Repl!(Foo) repl; 121 int b = 88; 122 repl.run("a = Foo()"); 123 repl.run("a.set()", b); 124 assert(repl.run("a.a").value == repl.Value(88)); 125 } 126 127 @safe unittest { 128 Repl!(Bar) repl; 129 130 repl.addGlobal("widgets", 4); 131 assert(repl.complete("", 0) == CompleteResult(["widgets"], 0, 0)); 132 assert(repl.complete("w", 0) == CompleteResult(["widgets"], 0, 1)); 133 } 134 135 @safe unittest { 136 Repl!(Bar) repl; 137 138 repl.addGlobal("bar", Bar()); 139 assert(repl.complete("b",1) == CompleteResult(["bar"], 0, 1)); 140 assert(repl.complete("bar.",4) == CompleteResult(["inner"], 4, 4)); 141 assert(repl.complete("bar.in",5) == CompleteResult(["inner"], 4, 6)); 142 } 143 144 @safe unittest { 145 Repl!(Bar) repl; 146 147 assert(repl.complete("b = Bar() 148 b. = 4 149 c = Bar()",13) == CompleteResult(["inner"], 13, 13)); 150 assert(repl.complete("b = Bar() 151 b.inner. = 4 152 c = Bar()",15) == CompleteResult(["b", "incr"], 19, 19)); 153 assert(repl.complete("b = Bar() 154 b.inn = 4 155 c = Bar()",14) == CompleteResult(["inner"], 12, 16)); 156 } 157 158 struct CompleteResult { 159 string[] matches; 160 ulong start; 161 ulong end; 162 } 163 164 struct Repl(Ts...) if (Ts.length > 0) { 165 alias makePointerType(T) = T*; 166 alias InnerTypes = discoverTypes!(Ts, double, int, string); 167 alias InnerTypesPtrs = staticMap!(makePointerType, AliasSeq!(InnerTypes, Ts)); 168 alias Value = SumType!(Ts, InnerTypes, InnerTypesPtrs, Void); 169 170 struct Void { 171 } 172 173 class Var { 174 Value value; 175 ParseTree tree; 176 bool error; 177 bool valid; 178 string message; 179 180 this(ParseTree tree) @safe { 181 this.tree = tree; 182 value = Void(); 183 } 184 185 this(Value value) @safe { 186 this.value = value; 187 valid = true; 188 } 189 190 this(ParseTree tree, Value value) @safe { 191 this(tree); 192 this.replaceValue(value); 193 valid = true; 194 } 195 196 void toString(scope void delegate(const(char)[]) sink) const @trusted { 197 sink("tree: "); 198 if (tree.input.length > 0) 199 sink(tree.text()); 200 else 201 sink("\n"); 202 sink("value: "); 203 sink(value.text()); 204 sink("\nerror: "); 205 sink(error.text()); 206 sink("\nvalid: "); 207 sink(valid.text()); 208 sink("\nmessage: "); 209 sink(message); 210 sink("\n"); 211 } 212 213 Var call(Ps...)(ParseTree tree, Ps ps) { 214 assert(tree.name == "ToyLanguage.Invoke"); 215 auto name = tree.matches[0]; 216 if (!valid) 217 return this; 218 try { 219 return new Var(tree, value.match!((ref t) => callIt(t, name, ps))); 220 } catch (Exception e) { 221 return Var.createError(tree, e); 222 } 223 } 224 225 Var access(ParseTree tree) @safe { 226 assert(tree.name == "ToyLanguage.Identifier"); 227 auto name = tree.matches[0]; 228 if (!valid) 229 return this; 230 try { 231 return new Var(tree, value.match!((ref t) => accessIt(t, name))); 232 } catch (Exception e) { 233 return new Var(tree, value).addError(tree, e); 234 } 235 } 236 237 Var assign(ParseTree tree, Var other) @safe { 238 assert(tree.name == "ToyLanguage.AssignmentExpression"); 239 if (!valid) { 240 replaceValue(other.value); 241 valid = true; 242 return this; 243 } 244 try { 245 return replaceValue(value.match!((ref t) => assignIt(t, other))); 246 } catch (Exception e) { 247 return this.addError(tree, e); 248 } 249 } 250 251 static Var createError(ParseTree tree, Exception e) @trusted { 252 return Var.createError(tree, cast(string)e.message); 253 } 254 255 static Var createError(ParseTree tree, string message) @safe { 256 return new Var(tree).addError(tree, message); 257 } 258 259 Var addError(ParseTree tree, string message) @safe { 260 this.message = "Error: %s in '%s'".format(message, tree.input[tree.begin .. tree.end]); 261 this.error = true; 262 return this; 263 } 264 265 Var addError(ParseTree tree, Exception e) @trusted { 266 return this.addError(tree, cast(string)e.message); 267 } 268 269 Var replaceValue(Value v) @trusted { 270 this.value = v; 271 return this; 272 } 273 274 string source() @safe { 275 return tree.input[tree.begin .. tree.end]; 276 } 277 } 278 Var[string] symbols; 279 280 void addGlobal(T)(string name, T v) { 281 auto var = new Var(Value(v)); 282 symbols[name] = var; 283 } 284 285 Var run(Ps...)(string code, Ps ps) { 286 auto result = parseToyLanguage(code); 287 if (!result.successful) 288 throw new Exception(result.matches.joiner("\n").text()); 289 290 return result.children.map!(child => executeStatements(child, ps)).joiner().reduce!((a,b) => a.error ? a : b); 291 } 292 293 CompleteResult complete(Ps...)(string code, ulong cursorPos, Ps ps) { 294 import std.array : array; 295 296 auto result = parseToyLanguage(code); 297 auto statements = result.children.map!(child => executeStatements(child, ps)).joiner().array(); 298 auto s = statements.filter!(s => s.tree.begin <= cursorPos && s.tree.end >= cursorPos); 299 300 if (s.empty) 301 return CompleteResult(getAllValidSymbols(), 0, code.length); 302 303 auto statement = s.front; 304 305 if (!statement.valid) { 306 return CompleteResult(getAllValidSymbols(), statement.tree.begin, statement.tree.end); 307 } 308 309 auto deepest = getLastChild(statement.tree); 310 auto matches = statement.value.match!((ref t) => getFieldsAndFunctions(t)); 311 312 return CompleteResult(matches, deepest.begin, deepest.end); 313 } 314 315 private: 316 string[] getAllValidSymbols() @safe { 317 import std.array : array; 318 return symbols.byKeyValue.filter!(pair => pair.value.valid).map!(pair => pair.key).array(); 319 } 320 321 Var assignToVar(Ps...)(ParseTree tree, Var lhs, Var rhs, Ps ps) { 322 if (lhs.error) 323 return lhs; 324 if (rhs.error) 325 return rhs; 326 if (!rhs.valid) 327 return rhs.addError(tree, "%s is unitialized".format(rhs.source)); 328 return lhs.assign(tree, rhs); 329 } 330 331 auto executeStatements(Ps...)(ParseTree tree, Ps ps) { 332 assert(tree.name == "ToyLanguage.Statements"); 333 return tree.children.map!(child => executeStatement(child, ps)); 334 } 335 336 Var dereference(Ps...)(Var var, Ps ps) { 337 return var.replaceValue(var.value.match!((ref t){ 338 static if (isPointer!(typeof(t))) { 339 return Value(*t); 340 } else 341 return Value(t); 342 })); 343 } 344 345 Var executeStatement(Ps...)(ParseTree tree, Ps ps) { 346 if (!tree.successful) 347 return Var.createError(tree, tree.name); 348 switch (tree.name) { 349 case "ToyLanguage.AssignmentExpression": return dereference(executeAssignment(tree, ps)); 350 case "ToyLanguage.Expression": return dereference(evaluateExpression(tree, ps)); 351 default: assert(0); 352 } 353 } 354 355 Var executeAssignment(Ps...)(ParseTree tree, Ps ps) { 356 assert(tree.name == "ToyLanguage.AssignmentExpression"); 357 return assignToVar(tree, getVar(tree.children[0], ps), getVar(tree.children[1], ps)); 358 } 359 360 Var getVar(Ps...)(ParseTree tree, Ps ps) { 361 assert(tree.name == "ToyLanguage.Expression" || tree.name == "ToyLanguage.Value"); 362 if (tree.name == "ToyLanguage.Value") 363 return getValueVar(tree, ps); 364 return evaluateExpression(tree, ps); 365 } 366 367 Var getValueVar(Ps...)(ParseTree tree, Ps ps) { 368 assert(tree.name == "ToyLanguage.Value"); 369 switch (tree.children[0].name) { 370 case "ToyLanguage.Integer": return new Var(tree, Value(tree.matches[0].to!int)); 371 case "ToyLanguage.Float": return new Var(tree, Value(tree.matches[0].to!double)); 372 case "ToyLanguage.String": return new Var(tree, Value(tree.matches[0][1..$-1])); 373 default: assert(0); 374 } 375 } 376 377 Var evaluateExpression(Ps...)(ParseTree tree, Ps ps) { 378 assert(tree.name == "ToyLanguage.Expression"); 379 Var object = evaluateGlobal(tree.children[0], ps); 380 foreach (child; tree.children[1 .. $]) { 381 if (child.name == "ToyLanguage.Invoke") 382 object = object.call(child, ps); 383 else if (child.name == "ToyLanguage.Identifier") 384 object = object.access(child); 385 else { 386 auto o = new Var(tree,object.value); 387 return o.addError(tree, "Invalid"); 388 } 389 } 390 return object; 391 } 392 393 Var evaluateGlobal(Ps...)(ParseTree tree, Ps ps) { 394 assert(tree.name == "ToyLanguage.Invoke" || tree.name == "ToyLanguage.Identifier"); 395 if (tree.name == "ToyLanguage.Invoke") { 396 static foreach(T; Ts) { 397 static if (isAggregateType!T) { 398 if (__traits(identifier, T) == tree.matches[0]) 399 return new Var(Value(T.init)); 400 } 401 } 402 return Var.createError(tree, "type %s not found".format(tree.matches[0])); 403 } 404 if (auto p = tree.matches[0] in symbols) 405 return (*p); 406 auto object = new Var(tree); 407 symbols[tree.matches[0]] = object; 408 return object; 409 } 410 411 static auto callIt(T, Ps...)(ref T t, string name, Ps ps) { 412 alias BaseType = getBaseType!T; 413 static if (isAggregateType!BaseType || is(BaseType == class)) { 414 static foreach(fun; getFunctions!(BaseType)) {{ 415 enum funName = __traits(identifier, fun); 416 if (name == funName) { 417 alias returnType = ReturnType!(__traits(getMember, t, funName)); 418 static if (is(returnType == void)) { 419 callFunction!(T, void, funName, Ps)(t, ps); 420 return Value(Void()); 421 } else { 422 return Value(callFunction!(T, returnType, funName, Ps)(t, ps)); 423 } 424 } 425 }} 426 throw new Exception("method '%s' is not available on type %s".format(name, __traits(identifier, BaseType))); 427 } else 428 throw new Exception("cannot call a method on a %s".format(BaseType.stringof)); 429 return Value(Void()); // needed for inference 430 } 431 432 static auto accessIt(T)(ref T t, string name) { 433 alias BaseType = getBaseType!(T); 434 static if (isAggregateType!BaseType && !is(BaseType : Nullable!P, P)) { 435 static foreach(member; getFields!BaseType) { 436 if (name == member) { 437 return Value(&__traits(getMember, t, member)); 438 } 439 } 440 throw new Exception("property '%s' is not available on type %s".format(name, __traits(identifier, BaseType))); 441 } else 442 throw new Exception("cannot access a property on a %s".format(BaseType.stringof)); 443 return Value(Void()); // needed for inference 444 } 445 446 static auto assignIt(T)(ref T t, Var other) { 447 static if (isPointer!T) { 448 other.value.match!((T otherT){ 449 *t = *otherT; 450 },(ref PointerTarget!T otherT){ 451 *t = otherT; 452 },(ref incompatible){ 453 throw new Exception("cannot assign type %s to type %s".format(typeof(incompatible).stringof, PointerTarget!(T).stringof)); 454 }); 455 return Value(t); 456 } else 457 return other.value; 458 } 459 460 static string[] getFieldsAndFunctions(T)(ref T t) { 461 static if (is(T == Void)) 462 return []; 463 else static if (isAggregateType!T || is(T == class)) { 464 alias Fields = getFields!T; 465 alias FunctionSymbols = getFunctions!T; 466 alias Functions = staticMap!(getIdentifier, FunctionSymbols); 467 static if (Fields.length == 0 && Functions.length == 0) 468 return []; 469 else static if (Fields.length == 0) { 470 enum string[Functions.length] fieldsAndFunctions = [Functions]; 471 } else static if (Functions.length == 0) { 472 enum string[Fields.length] fieldsAndFunctions = [Fields]; 473 } else 474 enum string[Fields.length + Functions.length] fieldsAndFunctions = [Fields, Functions]; 475 return fieldsAndFunctions[]; 476 } else { 477 return []; 478 } 479 } 480 481 static R callFunction(T, R, string name, Ps...)(auto ref T t, Ps ps) { 482 alias fun = __traits(getMember, t, name); 483 alias parameters = Parameters!(fun); 484 alias ResultType = ReturnType!(fun); 485 static if (parameters.length > 0) { 486 static if (__traits(compiles, __traits(getMember, t, name)(ps))) { 487 return __traits(getMember, t, name)(ps); 488 } 489 throw new Exception("cannot call method '%s' on type %s".format(name, T.stringof)); 490 } else { 491 return __traits(getMember, t, name)(); 492 } 493 } 494 } 495 496 @safe unittest { 497 Foo foo = Foo(); 498 Repl!(Foo).callIt(foo, "doStuff"); 499 assert(foo.a == 5); 500 } 501 502 // get the most deepest, last child of the tree 503 ParseTree getLastChild(ref ParseTree p) @safe { 504 if (p.children.length == 0) 505 return p; 506 return getLastChild(p.children[$-1]); 507 } 508 509 // strips the pointer 510 template getBaseType(T) { 511 static if (isPointer!T) 512 alias getBaseType = PointerTarget!T; 513 else 514 alias getBaseType = T; 515 } 516 517 enum getIdentifier(alias T) = __traits(identifier, T); 518 519 // gets all functions from T as symbols 520 template getFunctions(T) { 521 alias getSymbol(string name) = AliasSeq!(__traits(getMember, T, name))[0]; 522 enum canAlias(string name) = __traits(compiles, getSymbol!name); 523 alias members = __traits(allMembers, T); 524 alias hasTSymbol = ApplyLeft!(hasMember, T); 525 alias symbols = staticMap!(getSymbol, Filter!(canAlias, Filter!(hasTSymbol, AliasSeq!(members)))); 526 alias getFunctions = Filter!(isFunction, symbols); 527 } 528 529 // returns all public fields of T 530 template getFields(T) { 531 alias hasField(string name) = hasMember!(T, name); 532 enum isPublic(string name) = __traits(getProtection, __traits(getMember, T, name)) == "public"; 533 alias getFields = Filter!(isPublic, Filter!(hasField, FieldNameTuple!(T))); 534 } 535 536 // Recursively get all types of the fields and return values of each function of each item in Ts 537 template discoverTypes(Ts...) { 538 enum isNotVoid(T) = !is(T == void); 539 static if (Ts.length == 0) { 540 alias discoverTypes = AliasSeq!(); 541 } else static if (Ts.length == 1) { 542 alias T = Ts[0]; 543 static if (!isAggregateType!T && !(is(T == class))) { 544 alias discoverTypes = AliasSeq!T; 545 } else static if(is(T : Nullable!P, P)) { 546 alias discoverTypes = AliasSeq!(Nullable!P, discoverTypes!P); 547 } else { 548 alias typeOfField(string name) = typeof(__traits(getMember, T, name)); 549 alias hasField(string name) = hasMember!(T, name); 550 enum isNoT(Other) = !is(T == Other); 551 alias fieldTypes = Filter!(isNoT, staticMap!(typeOfField, getFields!T)); 552 alias functionReturnTypes = Filter!(isNoT, staticMap!(ReturnType, getFunctions!(T))); 553 alias Types = AliasSeq!(fieldTypes, discoverTypes!fieldTypes, functionReturnTypes, discoverTypes!(functionReturnTypes)); 554 alias discoverTypes = Filter!(isNotVoid, NoDuplicates!(Types)); 555 } 556 } else { 557 alias discoverTypes = Filter!(isNotVoid, NoDuplicates!(AliasSeq!(discoverTypes!(Ts[0]), discoverTypes!(Ts[1 .. $])))); 558 } 559 } 560